package com.easyedu.dean.service;

import com.alibaba.fastjson.JSONObject;
import com.easyedu.core.dao.ClassRoomDao;
import com.easyedu.core.dao.CultivationSchemeDao;
import com.easyedu.core.dao.OpenCourseDao;
import com.easyedu.core.dao.StudentSelectDao;
import com.easyedu.core.entity.BaseError;
import com.easyedu.core.entity.CacheError;
import com.easyedu.core.entity.DbError;
import com.easyedu.core.entity.DeanServiceError;
import com.easyedu.core.entity.dto.TimeTableIndex;
import com.easyedu.core.entity.po.CultivationScheme;
import com.easyedu.core.entity.po.HasArrangeCourse;
import com.easyedu.core.entity.po.OpenCourse;
import com.easyedu.core.entity.po.OpenCourseTime;
import com.easyedu.core.entity.po.StudentSelect;
import com.easyedu.core.entity.po.Teacher;
import com.easyedu.core.exception.EasyEduException;
import com.easyedu.core.redis.RedisUtils;
import com.easyedu.core.util.BuildRedisKey;
import com.easyedu.dean.entity.dto.request.ReqCreateOpenCourse;
import com.easyedu.dean.entity.dto.request.ReqQueryHasArrangeCourse;
import com.easyedu.dean.entity.dto.response.RespQueryHasArrangeCourse;
import com.easyedu.dean.entity.dto.response.RespTimeTableIndex;
import com.easyedu.dean.entity.dto.response.RespTimeTableIndex.BuildObj;
import com.easyedu.dean.entity.dto.response.RespTimeTableIndex.WeekObj;
import com.easyedu.dean.entity.excel.TimeTable2Excel;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author linxi
 * @date 2022/3/20
 * @describe 开课服务
 **/
@Service
public class OpenCourseService {

     private static final String STR_FORMAT_2 = "00";
     private static final String STR_FORMAT_3 = "000";
     private static final String STR_FORMAT_4 = "0000";
     private static final String FIRST_SEMESTER = "第一学期";

     private static final int COURSE_NOT_OPEN = 1;

     private final OpenCourseDao openCourseDao;

     private final CultivationSchemeDao cultivationSchemeDao;

     private final StudentSelectDao studentSelectDao;

     private final RedisUtils redisUtils;

     private final ClassRoomDao classRoomDao;

     private final TeacherService teacherService;

     public OpenCourseService(OpenCourseDao openCourseDao, CultivationSchemeDao cultivationSchemeDao,
             RedisUtils redisUtils, StudentSelectDao studentSelectDao, ClassRoomDao classRoomDao,
             TeacherService teacherService) {

          this.openCourseDao = openCourseDao;
          this.cultivationSchemeDao = cultivationSchemeDao;
          this.redisUtils = redisUtils;
          this.studentSelectDao = studentSelectDao;
          this.classRoomDao = classRoomDao;
          this.teacherService = teacherService;
     }

     @Transactional(rollbackFor = Exception.class)
     public void createOpenCourse(ReqCreateOpenCourse reqCreateOpenCourse) {

          Teacher teacher = teacherService.getTeacherById(reqCreateOpenCourse.getTeacherId());
          if (teacher == null || teacher.getTeacherId() == 0) {
               throw new EasyEduException(DeanServiceError.ERR_TEACHER_NOT_EXIST);
          }

          CultivationScheme scheme = cultivationSchemeDao.getById(reqCreateOpenCourse.getCultivationSchemeId());
          if (scheme == null || scheme.getCultivationSchemeId() == 0) {
               throw new EasyEduException(DeanServiceError.ERR_CULTIVATION_SCHEME_NOT_EXIST);
          }
          OpenCourse openCourse = new OpenCourse();
          openCourse.setCultivationSchemeId(scheme.getCultivationSchemeId());

          int year = Integer.parseInt(scheme.getGrade());
          switch (scheme.getSchoolYear()) {
               case "一年级": {
                    year += 1;
                    break;
               }
               case "二年级": {
                    year += 2;
                    break;
               }
               case "三年级": {
                    year += 3;
                    break;
               }
               case "四年级": {
                    year += 4;
                    break;
               }
               default: {
                    throw new EasyEduException(BaseError.ILLEGAL_DATA, "数据非法，请检查培养方案学年");
               }
          }
          String semester = scheme.getSemester();
          if (semester.equals(FIRST_SEMESTER)) {
               year--;
          }
          String courseNumber;
          DecimalFormat decimalFormat4 = new DecimalFormat(STR_FORMAT_4);
          DecimalFormat decimalFormat3 = new DecimalFormat(STR_FORMAT_3);
          DecimalFormat decimalFormat2 = new DecimalFormat(STR_FORMAT_2);
          courseNumber = decimalFormat4.format(year);
          if (semester.equals(FIRST_SEMESTER)) {
               courseNumber = courseNumber + decimalFormat2.format(1);
          } else {
               courseNumber = courseNumber + decimalFormat2.format(2);
          }
          courseNumber = courseNumber + decimalFormat3.format(scheme.getBelongInstituteId()) + decimalFormat2
                  .format(scheme.getBelongDepartmentId());
          openCourse.setOpenYear(year);
          openCourse.setSchoolYear(scheme.getSchoolYear());
          openCourse.setSemester(scheme.getSemester());
          openCourse.setOpenInstituteId(scheme.getOpenInstituteId());
          openCourse.setOpenInstituteName(scheme.getOpenInstituteName());
          openCourse.setTeachInstituteId(teacher.getInstituteId());
          openCourse.setTeachInstituteName(teacher.getInstituteName());
          openCourse.setOpenDepartmentId(scheme.getBelongDepartmentId());
          openCourse.setOpenDepartmentName(scheme.getBelongDepartmentName());
          openCourse.setCourseCode(scheme.getCourseCode());
          openCourse.setCourseName(scheme.getCourseName());
          openCourse.setTeacherId(teacher.getTeacherId());
          openCourse.setTeacherName(teacher.getName());
          openCourse.setCredit(scheme.getCredit());
          openCourse.setTheoryClassHour(scheme.getTheoryClassHour());
          openCourse.setExperimentClassHour(scheme.getExperimentClassHour());
          openCourse.setTotalClassHour(scheme.getTotalClassHour());
          openCourse.setUsualScoreRatio(scheme.getUsualScoreRatio());
          openCourse.setExperimentScoreRatio(scheme.getExperimentScoreRatio());
          openCourse.setExamScoreRatio(scheme.getExamScoreRatio());
          openCourse.setCapacity(reqCreateOpenCourse.getCapacity());
          openCourse.setFreeCapacity(0);
          openCourse.setIsOpen(COURSE_NOT_OPEN);
          openCourse.setIsRepeat(reqCreateOpenCourse.getRepeat());
          boolean flag = false;
          for (int i = 1; i < 9950; i += 50) {
               int r = (int) (Math.random() * 50) + i;
               if (openCourseDao.getByCourseNumber(courseNumber + decimalFormat4.format(r)) == null) {
                    openCourse.setCourseNumber(courseNumber + decimalFormat4.format(r));
                    Integer isCommit = openCourseDao.createOpenCourse(openCourse);
                    if (isCommit != 1) {
                         throw new EasyEduException(DbError.Err_INSERT_DB_ERROR, "新增课程失败，请重试");
                    }
                    return;
               }
               if (i > 9900) {
                    if (flag) {
                         throw new EasyEduException(DeanServiceError.ERR_NOT_ENOUGH_COURSE_NUMBER, "课号空余不足，请联系管理员");
                    }
                    i = 0;
                    flag = true;
               }
          }
     }

     public List<OpenCourse> getOpenCourse(OpenCourse openCourse) {

          return openCourseDao.getOpenCourse(openCourse);
     }

     public void deleteOpenCourse(Long openCourseId) {

          OpenCourse openCourseById = openCourseDao.getOpenCourseById(openCourseId);
          if (openCourseById == null) {
               throw new EasyEduException(DeanServiceError.ERR_OPEN_COURSE_NOT_EXIST);
          }
          Integer integer = openCourseDao.deleteById(openCourseId);
          if (integer != 1) {
               throw new EasyEduException(DbError.Err_DELETE_DB_ERROR, "删除已开课程失败");
          }
     }

     @Transactional(rollbackFor = Exception.class)
     public String openOpenCourse(List<Long> ids) {

          Integer integer = openCourseDao.updateCourseSetOpen(ids);
          HasOpenCourseToRedis();
          return "共" + ids.size() + "门课程，已成功开课" + integer + "门";
     }

     @Transactional(rollbackFor = Exception.class)
     public void HasOpenCourseToRedis() {

          List<OpenCourse> openCourseList = new CopyOnWriteArrayList<>(openCourseDao.getHasOpenCourse());
          //写入缓存
          redisUtils.flashDb();
          openCourseList.parallelStream().forEach(openCourse -> {
               long openCoursesId = openCourse.getOpenCoursesId();
               long capacity = openCourse.getFreeCapacity();
               String courseNumber = openCourse.getCourseNumber();
               long openInstituteId = openCourse.getOpenInstituteId();
               long openDepartmentId = openCourse.getOpenDepartmentId();
               long openYear = openCourse.getOpenYear();
               long teacherId = openCourse.getTeacherId();
               String courseMsgJson = JSONObject.toJSONString(openCourse);

               //课程容量
               Integer aInteger =
                       redisUtils.append(BuildRedisKey.buildCourseCapacity(courseNumber), String.valueOf(capacity));
               if (aInteger == 0) {
                    throw new EasyEduException(CacheError.ERR_INSERT_CACHE, "课程添加至缓存失败，请重试！");
               }
               //可选课程
               Long aLong = redisUtils
                       .lLeftPush(BuildRedisKey.buildCourseSelection(openInstituteId, openDepartmentId, openYear),
                               courseMsgJson);
               if (aLong == 0) {
                    throw new EasyEduException(CacheError.ERR_INSERT_CACHE, "添加可选课程信息至缓存失败，请重试");
               }
               //教师所开课程
               aLong = redisUtils.lLeftPush(BuildRedisKey.buildCourseTeacher(teacherId), courseMsgJson);
               if (aLong == 0) {
                    throw new EasyEduException(CacheError.ERR_INSERT_CACHE, "添加教师所交课程信息至缓存失败，请重试");
               }
               //课程信息
               aInteger = redisUtils.append(BuildRedisKey.buildCourse(courseNumber), courseMsgJson);
               if (aInteger == 0) {
                    throw new EasyEduException(CacheError.ERR_INSERT_CACHE, "添加课程信息至缓存失败");
               }

               //刷新选课学生列表
               List<StudentSelect> byOpenCourseId = studentSelectDao.getByOpenCourseId(openCoursesId);
               String studentListKey = BuildRedisKey.buildCourseStudentList(courseNumber);
               redisUtils.delete(studentListKey);
               byOpenCourseId.parallelStream().forEach(studentSelect -> redisUtils
                       .lLeftPush(studentListKey, String.valueOf(studentSelect.getStudentNumber())));
          });
     }

     public String closeOpenCourse(List<Long> ids) {

          Integer integer = openCourseDao.updateCourseSetClose(ids);
          return "共" + ids.size() + "门课程，已关闭开课" + integer + "门";
     }

     public void openSelectionCourseSystem() {

          try {
               redisUtils.set("is_open_selection_system", "true");
          } catch (Exception e) {
               throw new EasyEduException(DeanServiceError.ERR_OPEN_COURSE_SELECTION, "选课系统开放失败，请重试");
          }
     }

     public void closeSelectionCourseSystem() {

          try {
               redisUtils.delete("is_open_selection_system");
          } catch (Exception e) {
               throw new EasyEduException(DeanServiceError.ERR_CLOSE_COURSE_SELECTION, "选课系统关闭失败，请重试");
          }
     }

     public RespTimeTableIndex getTimeTableIndex(Integer year, String semester) {

          //获取所有课程的日期
          List<TimeTableIndex> openCourseTimes = openCourseDao.getOpenCourseTimeAndBuilding(year, semester);
          //周,教学楼,教室
          Map<Integer, Map<String, List<String>>> timeTableIndexMap = new TreeMap<>();
          //对课程时间归类
          openCourseTimes.forEach(timeTableIndex -> {
               Map<String, List<String>> buildMap =
                       timeTableIndexMap.getOrDefault(timeTableIndex.getOpenDay(), new TreeMap<>());
               List<String> classRoomList = buildMap.getOrDefault(timeTableIndex.getBelongBuild(), new ArrayList<>());
               classRoomList.add(timeTableIndex.getClassroom());
               buildMap.put(timeTableIndex.getBelongBuild(), classRoomList);
               timeTableIndexMap.put(timeTableIndex.getOpenDay(), buildMap);
          });
          RespTimeTableIndex resp = new RespTimeTableIndex();
          resp.setWeekList(new ArrayList<>());
          timeTableIndexMap.forEach((week, buildMap) -> {
               WeekObj weekObj = new WeekObj();
               weekObj.setWeek(week);
               weekObj.setBuildList(new ArrayList<>());
               buildMap.forEach((build, classRoomList) -> {
                    BuildObj buildObj = new BuildObj(build, classRoomList);
                    weekObj.getBuildList().add(buildObj);
               });
               resp.getWeekList().add(weekObj);
          });
          return resp;
     }

     public RespQueryHasArrangeCourse getHasArrangeCourseInfo(ReqQueryHasArrangeCourse req) {

          int week = req.getWeek();
          String classroom = req.getClassroom();
          //获取已排课课程信息
          List<HasArrangeCourse> hasArrangeCourses = openCourseDao
                  .getHasArrangeCourse(req.getOpenYear(), req.getSemester(), req.getBuilding(), week, classroom);

          if (hasArrangeCourses == null || hasArrangeCourses.isEmpty()) {
               throw new EasyEduException(DeanServiceError.ERR_CLASS_ROOM_EMPTY, "此教室没有任何课程");
          }
          RespQueryHasArrangeCourse resp = new RespQueryHasArrangeCourse();

          resp.setClassroom(classroom);
          resp.setCourseInfos(hasArrangeCourses);
          return resp;
     }
     public List<TimeTable2Excel> getTimeTable2Excel(Integer openYear, String semester) {
          List<HasArrangeCourse> timeTableAndBuild = openCourseDao.getTimeTableAndBuild(openYear, semester);
          List<TimeTable2Excel> res = new ArrayList<>();
          for (HasArrangeCourse course : timeTableAndBuild) {
               res.add(TimeTable2Excel.builder().classroomId(course.getClassroomId()).buildName(course.getBuildName())
                       .classroomName(course.getClassroomName()).courseName(course.getCourseName())
                       .courseNumber(course.getCourseNumber()).openWeek(course.getOpenWeek())
                       .openDay(course.getOpenDay()).openLesson(course.getOpenLesson())
                       .teacherName(course.getTeacherName()).build());
          }
          return res;
     }

     /**
      * 删除课表
      * @param year
      * @param semester
      */
     public void deleteTimeTable(Integer year, String semester) {
          List<OpenCourseTime> openCourseTime = openCourseDao.getOpenCourseTime(year, semester);
          if (openCourseTime != null && !openCourseTime.isEmpty()) {
              openCourseDao.deleteOpenCourseTime(year, semester);
          }
          throw new EasyEduException(DeanServiceError.ERR_TIME_TABLE_NOT_EXIST);
     }
}
